/**
 * @class   wosh::devices::JabberGlooxBundle
 * @brief   \ref wosh::interfaces::services::CommunicatorService "Communicator Service"
 *          for Instant Messaging. Jabber/XMPP protocol, based on \c gloox library.
 *
 * This \ref wosh::interfaces::Device device is a communication service and acts as adapter between WOSH (communication) 
 * and Jabber/XMPP protocol, compatible with GTalk and plenty of open servers.
 *
 * Compatible with POSIX and Windows systems.
 *
 * Tested with Linux Debian (lenny, etch, Ubuntu8,9), Windows XP and Google Talk service.
 *
 * \par Requirements:
 *  Implementation is based on <a href="http://camaya.net/gloox/">gloox library</a> (multi-platform, open source, C++).
 *  Minimum required version is 0.9.x (available on Debian Lenny 5.0), last supported
 *  version is 1.0.x (available on gloox website).
 * 
 * There are some changes (0.9 => 1.0) that broken backward-compatibility, so two different wrappers are
 * available, they both share the base abstract class JabberGlooxImpl.
 *
 * JabberGlooxBundle has been tested on Linux Debian (lenny, etch, Ubuntu8,9) and Google Talk service,
 * using official version 0.9.x (from repository), wrapper is implemented by JabberGloox09.
 * On Windows XP, you have to build gloox library (check /lib/3rdparty/gloox for information),
 * JabberGloox10 is the wrapper for version 1.x.
 *
 * on Linux, libgloox is available in Debian-based repositories, you may easily install executing:
 * \verbatim
alex@linbox:/home/alex/$ sudo apt-get install libgloox-dev
\endverbatim
 * At the time of writing, Debian Lenny provides gloox version 0.9.9.5-1.
 * 
 * In order to have the jabber client connected to XMPP server (such as GTalk-service) you need
 * to setup address and account information. Most important Properties are:
 *  - \c ServerAddress - the address (IP or hostname) of the XMPP server, such as talk.google.com
 *  - \c ServerPort - the port of the XMPP service, standard port is 5222
 *  - \c UserName - the username of the (bot) account (on GTalk it looks like "USER-NAME@gmail.com" )
 *  - \c UserPassword - the password of the account
 *
 * A sample configuration is available: <a href="../../etc/wosh/bundles-available/380_JabberGloox.conf">380_JabberGloox.conf</a>
 *
 * \note
 *  You should create (or keep) a separate account for this service (don't use your personal account!),
 *  for example I named it something like "my-home@gmail.com". This account will look just like another
 *  friend in your contact list, acting as proxy and letting you 'talk' with WOSH system.
 *
 *  Google Talk service may block the bot-account sometimes, then you may need to login manually
 *  through a web browser (while or before starting the service).
 *
 * Once the above properties have been configured properly, the service is able to connect, send
 * and receive message from friends.
 * But the JabberGlooxBundle is also a \ref wosh::interfaces::services::CommunicatorService "Communicator Service",
 * so it implements some standard methdos/functionalities and follow a set of spefication in order
 * to cooperate with \ref wosh::services::CommunicationManagerBundle "Communication Manager".
 *
 * \note
 *  Communication is an big topic in WOSH, to undestand the architecture and global flows,
 *  you should read \ref page_framework_communication.
 *
 * \par In \c very short, outgoing message Flow
 *  In WOSH, messages (real messages, not wosh::Message) are based on wosh::Notification interface,
 *  sender and recipent users are WOSH usernames, so first we need a way to map it to the jabber
 *  account (example: alex => "alex-rules@gmail.com").
 *  This problem is solved by wosh::UserManager: JabberGlooxBundle reads user' property
 *  to resolve his jabber-account or search the user matching the jabber-account for 
 *  reverse lookup. By default the property-key is 'JabberAccount', it may be changed setting
 *  the 'UserAccountKey' property of the service.
 *  Users' database is stored in /var/database/users folder, XML format. Here is an example
 *  of information required by JabberGlooxBundle to map accounts:
 * \verbatim
    <wosh::User name="alex" id="1020" >
      <properties>
        <property key="JabberAccount">alex.rules@gmail.com</property>
      </properties>
    </wosh::User>
\endverbatim
 *  This archicture allows users to have multiple (independent) accounts and multiple
 *  JabberGlooxBundle instance at same time (just having different 'UserAccountKey').
 *
 * \msc
CommunicationManager, UserManager, JabberGloox, ServerXMPP;
CommunicationManager=>JabberGloox [ label="send notification", URL="\ref wosh::devices::JabberGlooxBundle::notify()" ];
--- [ label=" Notification Flow started " ];
|||;
JabberGloox->JabberGloox [ label="validate notification (textual, !empty)" ];
--- [ label=" if target is wosh-user, resolve to jabber-account " ];
JabberGloox=>UserManager [ label="retrieve user property", URL="\ref wosh::UserManager::getUserProperty()" ];
JabberGloox<-UserManager [ label="return jabber-account (or empty)" ];
--- [ label=" IF target resolved as jabber-account " ];
JabberGloox=>ServerXMPP [ label="send message" ];
|||;
--- [ label=" Notification Flow completed  " ];
CommunicationManager<-JabberGloox [ label="ack notification-result" ];
 * \endmsc
 *
 * \par In \c very short, outgoing message Flow
 *  Moreover the incoming messages (from XMPP network) must be redirected to some WOSH user.
 *  The previous flow is capable of reverse resolution, so we easily recognize the sender,
 *  but what about target user? answer is kind of philosophical:
 *  Communicator services (themselves) are not worldwide message routers, but a sort of
 *  endpoint/bridge. The remote user wants to interact with the WOSH system itself or
 *  in some case with a custom component of it (for example: the Butler service).
 *  Here comes that the target user is usually 'wosh' (a virtual user referring to WOSH itself).
 *  By default the target user is shown as status message.
 *
 *  In other words: incoming messages are interpreted as sent to current target user.
 *
 * @todo [...] SESSIONs ..
 *

 * \par Internal commands
 *  JabberGlooxBundle supports some internal commands (not parsed as messages) for quick
 *  (re)configuration and debugging. Internal commands always starts with "%" and are upcase.
 *   - \c HELP - prints a minimal help screen about these commands
 *   - \c INFO - prints information about yourself and current settings
 *   - \c TARGET - get/set the target user ('wosh' by default)
 *   - \c LANG - get/set your current language
 *   - \c PROTO - get/set the protocol used (SHELL by default)
 *
 * JabberGlooxBundle acts mostly as wrapper (bundle-adapter), real implementation is JabberGloox10 (gloox-1.x) or JabberGloox09 (gloox-0.9.x).
 *
 * @bug     On remote disconnection, the bot won't reconnect (or try to force (dis)connection with callback/same-thread)
 *
 * @see     \ref Devices, wosh::interfaces::Device
 * @see     http://camaya.net/gloox/
 * @ingroup Devices
 ****************************************************************************
 * @version 0.8.384 $Id: JabberGlooxBundle.h 2879 2010-08-09 02:41:13Z alex $
 * @date    Released Stable on 2010-08-05
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Bundles_Devices_JabberGloox_H__
 #define __WOSH_Bundles_Devices_JabberGloox_H__

 #include <core/BundleGeneric.h>
 #include <core/Thread.h>
 #include <core/Containers.h>

 #include <core/Communicator.h>
 #include <framework/communication/ContactInfo.h>
 #include <framework/communication/TextMessage.h>
 #include <framework/communication/SessionProxy.h>

 #include <framework/security/SecurityAuthentication3rdParty.h>

 #include <interfaces/services/CommunicatorService.h>
 #include <interfaces/devices/JabberGloox.h>

 #include "JabberGlooxImpl.h"


using namespace wosh::communication;
namespace wosh {
 namespace devices {

 #define _JabberGlooxBundle_VERSION			0.8384


class JabberGlooxBundle :	public wosh::BundleGeneric,
							public virtual wosh::interfaces::services::CommunicatorService, 
							public virtual wosh::interfaces::devices::JabberGloox, 
							public virtual IJabberGlooxListener,
							public virtual ISessionProxyListener
 {
	WOSH_BUNDLE(wosh::devices::JabberGlooxBundle)
	WOSH_BUNDLE_INTERFACE("wosh::interfaces::devices::JabberGloox")
	WOSH_BUNDLE_SINGLETON(0,0)

	typedef MapT<std::string, ContactInfo*> tStrContactInfoMap;


	public:
		/**
		 * @brief   Empty constructor, init bundle and vars, Properties and Methods.
		 *          Allocate the internal JabberGlooxImpl
		 */
		JabberGlooxBundle();
		/**
		 * @brief   Empty destructor, may (synch) stop bundle and internal threads when still running
		 */
		virtual ~JabberGlooxBundle();


	public:
		virtual WRESULT bundleStart();
		virtual WRESULT bundleStop();

		virtual void busMessage( const Message& message, const Bus* source );

		virtual bool updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source );

		inline wosh::interfaces::Device::DEVICE_STATE getDeviceState() const {
			if ( this->jabbWorker->isConnected() )
				return wosh::interfaces::Device::LINKED;
			else
				return wosh::interfaces::Device::UNLINKED;
			// wosh::interfaces::Device::BROKEN); }
		 }

//@}
/** @name JabberGloox Interface
 * @{
 ******************************************************************************/
	public:
		inline const std::string& getServerAddress() const		{ return this->jabbWorker->getServerAddress(); }
		inline int getServerPort() const						{ return this->jabbWorker->getServerPort(); }
		inline const std::string& getAccountName() const		{ return this->jabbWorker->getAccountName(); }
		inline int getPriority() const							{ return this->jabbWorker->getPriority(); }

		inline wosh::security::SecurityTokenProvider3rdparty& getCredentialsIO() { return this->CredentialsIO; }

		inline bool setServer( const std::string& address, int port ) { return this->jabbWorker->setServer(address, port); }
		inline bool setAccount( const std::string& username, const std::string& password ) { return this->jabbWorker->setAccount(username, password); }
		inline bool setPriority( int value )					{ return this->jabbWorker->setPriority(value); }

		WRESULT doContactsSynch( bool add, bool remove );

//@}
/** @name Communicator Interface
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Check if user is online now (logged in current server)
		 * @param username [in] wosh-username to lookup
		 * @return  true if user is online; false else
		 */
		bool isUserReachable( const std::string& username );

		/**
		 * @brief   Check if user is registered to current server
		 * @param username [in] wosh-username to lookup
		 * @return  true if user is in friends' list
		 */
		bool isUserRegistered( const std::string& username );

		/**
		 * @brief   Get the timestamp of last(seen) online-status of given user
		 * @param username [in] wosh-username to lookup
		 * @return  epoc timestamp if user is still online, negative epoch if user is actually offline
		 */
		long getUserLastSeen( const std::string& username );

		/**
		 * @brief   Send a text message to given (wosh) username
		 * @param userWosh [in] recipent of the message (as WOSH username)
		 * @param message [in] message to be sent
		 * @return WRET_ERR_PARAM if account wasn't found, WRET_OK when message was sent, WRET_ERR_INTERNAL else.
		 * @see   getAccountNameFromUsername()
		 */
		WRESULT sendMessageToUser( const std::string& userWosh, const std::string& message );

		/**
		 * @brief   Send a text message to given account name [example: asd123@gmail.com]
		 * @param userID [in] recipent of the message
		 * @param message [in] message to be sent
		 * @return WRET_ERR_PARAM if account wasn't found, WRET_OK when message was sent, WRET_ERR_INTERNAL else.
		 */
		WRESULT sendMessageTo( const std::string& userID, const std::string& message );

		/**
		 * @brief   Send a Notification translated into a text message
		 * @param notification [in] the Notification to communicate
		 * @param notification_message [in] message containing the Notification to communicate
		 * @return WRET_ERR_PARAM if account wasn't found, WRET_OK when message was sent, WRET_ERR_INTERNAL else.
		 */
		WRESULT notify( const Notification* notification, const Message* notification_message = NULL );

//@}
/** @name IJabberGlooxListener Events
 * @{
 ******************************************************************************/
 	public:
		void jabbbot_UserStatus( const std::string& username, int status, const std::string& status_message, JabberGlooxImpl* bot );
		void jabbbot_Message( const std::string& username, const std::string& message, JabberGlooxImpl* bot );
		short jabbbot_SubscriptionChanged( const std::string& username, int status, JabberGlooxImpl* bot );
//@}
/** @name ISessionProxyListener Events
 * @{
 ******************************************************************************/
	public:
		void session_registration( short reg_unreg, wosh::communication::SessionProxy* session );
		WRESULT session_print_message( const std::string& message, wosh::communication::SessionProxy* session );
//@}
	private:
		Response* mmDoSendMessageToUser( const Message* requestMessage );

		Response* mmDoContactAdd( const Request* request );
		Response* mmDoContactRemove( const Request* request );
		Response* mmDoContactsSynch( const Request* request );

/** @name Internal methods
 * @{
 ******************************************************************************/
	protected:
		/**
		 * @brief   Create an account (in the local list)
		 * @param accountname [in] jabber-accont name
		 * @return pointer to the new Contact
		 */
		ContactInfo* checkCreateAccount_( const std::string& accountname );

		WRESULT setupUserSession( ContactInfo* info );

		/**
		 * @brief   Evaluate if message is an internal command (for the service itself)
		 *          Internal commands always starts with "%" and are upcase.
		 *          Available commands are:
		 *            - \c HELP - prints a minimal help screen about these commands
		 *            - \c INFO - prints information about yourself and current settings
		 *            - \c TARGET - get/set the target user ('wosh' by default)
		 *            - \c LANG - get/set your current language
		 *            - \c PROTO - get/set the protocol used (SHELL by default)
		 *
		 * @param message [in] message to be evaluated
		 * @param info [in] the sender of the message
		 * @return  true if the message is an internal command (so it won't be forwarded)
		 */
		bool parseInternalCommand( const std::string& message, ContactInfo* info );

		/**
		 * @brief   Retrieve account name from username
		 * @param username [in] wosh-username to lookup
		 * @return account name associated with wosh-username
		 * @see     UserManager::getUserProperty()
		 */
		std::string getAccountNameFromUsername( const std::string& username );

		/**
		 * @brief   Retrieve username from  account name
		 * @param accountname [in] account username to lookup
		 * @return wosh-username associated with account name
		 * @see     UserManager::findUserByProperty()
		 */
		std::string getUsernameFromAccountName( const std::string& accountname );

//@}
	protected:
		JabberGlooxImpl* jabbWorker;	///< real implementation (basled on libgloox)
		tStrContactInfoMap contacts;	///< contacts (friends) list: { name => info }
		BusConnector BusSecurity;		///< connector to Security bus (posting only)

		wosh::security::SecurityTokenProvider3rdparty CredentialsIO;


}; // class def

 }; // namespace devices
}; // namespace wosh

#endif //__WOSH_Bundles_Devices_JabberGloox_H__
